#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/root.h>
#include<sys/link.h>
#define BUFFER 1024



int ID;
int procN;
LinkCB_t *LinkFo, *LinkTo;
int clean;
int heap;
char message[BUFFER];
int all;
int end_fifo;

// структура для передаваемого сообщения
struct Mess
{
	int id;
	int all;
	char post[BUFFER]; 
};

// установка линков между процессорами
int setAllLink(void)
{
	int i; 
	int j; 
	int error;
	int id_to;
	i = 1234;
	
	
	if (ID = 0) //первый процессор
	{
		heap = 1;
		LinkTo = MakeLink(ID+1, i, &error);
		if (LinkTo==NULL) 
		{
			LogError(EC_ERROR, "ID", "\n make link error = %d", error);
			exit(1);
		}
		LinkFo = GetLink(procN-1, i, &error);
		if (LinkFo==NULL) 
		{
			LogError(EC_ERROR, "ID", "\n Get link error = %d", error);
			exit(1);
		}
		
	}
	else //остальные процессоры
	{
		LinkFo = GetLink(ID-1, i, &error);
		if (LinkFo==NULL) 
		{
			LogError(EC_ERROR, "ID", "\nGet link error = %d", error);
			exit(1);
		}
		id_to = ID+1;	
		if (ID == procN-1) { id_to = 0; } // установка линка между последним и первый
		LinkTo = MakeLink(id_to, i, &error);
		if (LinkTo==NULL) 
		{
			LogError(EC_ERROR, "ID", "\n make link error = %d", error);
			exit(1);
		}
	}
	clean = 1;
	return 0;
}


int EndFiFo(void) // завершение работы
{
   struct Mess AllMess;
   AllMess.id = 11;
   SendLink (LinkTo, (byte*) &AllMess, BUFFER);
   RecvLink (LinkFo, (byte*) &AllMess, BUFFER);
}

int AddFiFo(char lmess[BUFFER]) //добавить в FIFO
{
/*
 * Вoзвращаемые значения:
 * 10 - буфер полон
 * 9  - удачное помещение в буфер 
 *
 *
 * 
 */

   struct Mess AllMess;
   if (heap == 1) //если я управляющий
	{
		if (clean == 1) // 
		{
			memcpy(message,lmess,BUFFER);
         		all = 1;
		}
      		else 
      		{
         		AllMess.id = 1;
         		memcpy(AllMess.post,lmess,BUFFER);
		        if (all < procN)
         		{
            			SendLink (LinkTo, (byte*) &AllMess, BUFFER);
            			all = all+1;
            			RecvLink (LinkFo, (byte*) &AllMess, BUFFER);
            			return AllMess.id;
         		}
         		else return 10; 
      		}
	}
   else
   {
      if (clean == 1) 
      {
         AllMess.id = 2;
         memcpy(AllMess.post,lmess,BUFFER);
         SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         RecvLink (LinkFo, (byte*) &AllMess, BUFFER);
         if (AllMess.id == 1)
         {
            memcpy(message,AllMess.post,BUFFER);
            return 9;
         }
         return AllMess.id;

      }
      else 
      {
         AllMess.id = 3;
         memcpy(AllMess.post,lmess,BUFFER);
         SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         RecvLink (LinkFo, (byte*) &AllMess, BUFFER);
         return AllMess.id;
      }
   }

}

int RecvFiFo(char lmess[BUFFER])
{

/*
 * Возвращаемые значения:
 * 0 - данный удачно извлечены
 * 8 - буфер пуст
 *
 */
   struct Mess AllMess;
   if (heap == 1) 
   {
      if (clean == 1) 
      {
         return 8;
      }
      else 
      {
         memcpy(lmess,message,BUFFER);
         heap = 0;
         all = all-1;
         AllMess.id=5;
         AllMess.all=all;
         SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         RecvLink (LinkFo, (byte*) &AllMess, BUFFER);
         if (AllMess.id=5)
         {
            heap = 1;
         }

      }
   }
   else
   {
         AllMess.id = 6;
         SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         RecvLink (LinkFo, (byte*) &AllMess, BUFFER);
         if (AllMess.id == 5)
         {
            heap = 1;
            memcpy(lmess,AllMess.post,BUFFER);
         }
         if (AllMess.id == 7)
         {
            memcpy(lmess,AllMess.post,BUFFER);
         }
   }
   return 0;
}


int SRFiFo(void)
{
   struct Mess AllMess;
   struct Mess RecvMess;

   while (end_fifo == 0)
   {
   // цикл длится до тех пор, пока не поступит сообщения об окончании работы

      RecvLink (LinkFo, (byte*)&RecvMess, BUFFER);
      AllMess.id=RecvMess.id;      
      AllMess.all=RecvMess.all;      
      memcpy(AllMess.post,RecvMess.post,BUFFER);
      
      if (RecvMess.id == 1) // положить сообщение себе если пустой, иначе отправить дальше
      {
         if (clean == 1) 
         {
            memcpy(message,AllMess.post,BUFFER);
            AllMess.id=9;
            SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         }
         else 
         {
            SendLink (LinkTo, (byte*) &AllMess, BUFFER);

         }
      }
      if (RecvMess.id == 2) //если я не управляющий то отправить дальше
      {
         if (heap == 1) 
         {
            if (all < procN) // если буфер не полон то отправить с запросом на добавление, иначе отправить сообщение о полноте
            {
               all = all+1;
               AllMess.id=1;
               SendLink (LinkTo, (byte*) &AllMess, BUFFER);
            }
            else
            {
               AllMess.id = 10;
               SendLink (LinkTo, (byte*) &AllMess, BUFFER);
            }
         }
         else 
         {
            SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         }
      }

      if (RecvMess.id == 3) // отправка сообщения до первого пустого либо до управляющего в случае полного буфера
      {
         if (heap == 1) 
         {
            AllMess.id = 10;
            SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         }
         else 
         {
            if (clean == 1) 
            {
               memcpy(message,AllMess.post,BUFFER);
               AllMess.id=4;
               SendLink (LinkTo, (byte*) &AllMess, BUFFER);
            }
            else 
            {
               SendLink (LinkTo, (byte*) &AllMess, BUFFER);
            }
         }
      }

   if (RecvMess.id == 4) // сообщение было добавлено в буфер где-то до управляющего элемента
      {
         if (heap == 1) 
         {
            AllMess.id = 9;
            all = all+1;
            SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         }
         else 
         {
            SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         }
      }

   if (RecvMess.id == 5) // удаление из буфера,  0-й элемент становится управляющим если он пуст, иначе становится управляющий следующий и он отправляет соощение об удачном извлечении
      {
         if (AllMess.all == 0) 
         {
            SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         }
         else 
         {
            all = AllMess.all;
            heap = 1;
            AllMess.id=7; 
            SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         }
      }

   if (RecvMess.id == 6) // отправка сообщения из головы
      {
         if (heap == 1) 
         {
            memcpy(AllMess.post,message,BUFFER);
            AllMess.id = 7;
            heap = 0;
            all = all-1;
	    if (all == 0)
	    {	
                AllMess.id = 5;
	    }
            AllMess.all = all;
            SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         }
         else 
         {
            SendLink (LinkTo, (byte*) &AllMess, BUFFER);
         }
      }

   if (RecvMess.id == 7) // пересылка сообщения в случае удачной попытки его вытащить
       {
         SendLink (LinkTo, (byte*) &AllMess, BUFFER);
      }

   if (RecvMess.id == 9) // просто передать дальше - сообщение добавлено
      {
         SendLink (LinkTo, (byte*) &AllMess, BUFFER);
      }

   if (RecvMess.id == 10) // просто переслать дальше - буфер полон
      {
         SendLink (LinkTo, (byte*) &AllMess, BUFFER);
      }

   if (RecvMess.id == 11) // завершить работу
      {
         end_fifo = 1;
         SendLink (LinkTo, (byte*) &AllMess, BUFFER);
      }
   }
   return 0;
}

int main(int argc, char* argv[])
{

char recvBuf[BUFFER];
int res;

end_fifo = 0;
ID = GET_ROOT()->ProcRoot->MyProcID; 
procN = GET_ROOT()->ProcRoot->nProcs;
res = setAllLink();

if (ID == 0) 
{
   AddFiFo("Hello1");
   AddFiFo("Hello3");
   AddFiFo("Hello2");
   RecvFiFo(recvBuf);
   AddFiFo("Hello2");
   AddFiFo("Hello4");
   AddFiFo("HelloErr");
   RecvFiFo(recvBuf);
   RecvFiFo(recvBuf);
   RecvFiFo(recvBuf);
   RecvFiFo(recvBuf);
   EndFiFo();
}
else
{
   SRFiFo();
}

return 0;

}
